#ifndef _MUTEX_H_
#define _MUTEX_H_

#if _WIN32 || _WIN64
#else
#include <pthread.h>
#endif

#include <stdexcept>
#include "typedef.h"
#include "define.h"

MMOSDK_NAMESPACE_BEGIN(mmo)

// wrapper around the platform's native lock
class mutex : public mmo::no_copy
{
    public:
	mutex()
	{
#if _WIN32 || _WIN64
	    InitializeCriticalSection(&impl);
#else
	    int error_code = pthread_mutex_init(&impl, 0);
	    if(error_code) throw std::runtime_error("mutex ctor");
#endif
	}
	~mutex()
	{
#if _WIN32 || _WIN64
	    DeleteCriticalSection(&impl);
#else
	    pthread_mutex_destroy(&impl);
#endif
	}
    public:
	void lock()
	{
#if _WIN32 || _WIN64
	    EnterCriticalSection(&impl);
#else
	    pthread_mutex_lock(&impl);
#endif
	}
	bool try_lock()
	{
#if _WIN32 || _WIN64
	    return TryEnterCriticalSection(&impl) != 0;
#else
	    return pthread_mutex_lock(&impl) == 0;
#endif
	}
	void unlock()
	{
#if _WIN32 || _WIN64
	    LeaveCriticalSection(&impl);
#else
	    pthread_mutex_unlock(&impl);
#endif
	}
    private:
#if _WIN32 || _WIN64
	CRITICAL_SECTION impl;
#else
	pthread_mutex_t impl;
#endif
};

// it helps to avoid the common problem of forgetting to release lock
class scoped_lock : public mmo::no_copy
{
    public:
	scoped_lock() : my_mutex(0) {}

	scoped_lock(mmo::mutex &mtx)
	{
	    acquire(mtx);
	}

	~scoped_lock()
	{
	    if(my_mutex) release();
	}
    public:
	void acquire(mmo::mutex &mtx)
	{
	    mtx.lock();
	    my_mutex = &mtx;
	}
	bool try_acquire(mmo::mutex &mtx)
	{
	    bool res = mtx.try_lock();
	    if(res) my_mutex = &mtx;
	    return res;
	}
	void release()
	{
	    my_mutex->unlock();
	    my_mutex = 0;
	}
    private:
	mmo::mutex *my_mutex;
};

MMOSDK_NAMESPACE_END

#endif
